#include <crane/core/vec.hpp>
#include <string>
#include <iostream>
#include <sys/types.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <linux/if_packet.h>        // AF_PACKET
#include <string.h>


struct InterfaceAddress {
    int             family;
    std::string     address;
    int             prefix;
};

struct NetworkInterface {
    std::string                  name;
    std::string                  mac;
    crane::Vec<InterfaceAddress> addresses;
    unsigned int   loopback : 1       = 0;  
    unsigned int   broadcast : 1      = 0;
    unsigned int   multicast : 1      = 0;
    unsigned int   point_to_point : 1 = 0;
    unsigned int   up : 1             = 0;
    unsigned int   running : 1        = 0;
    unsigned int   dynamic : 1        = 0;
};


static void enumNetworkInterfaces(crane::Vec<NetworkInterface>& interfaces);
static void printNetworkInterfaces(const crane::Vec<NetworkInterface>& interfaces);


int main(int argc, char* argv[])
{
    crane::Vec<NetworkInterface> interfaces;
    enumNetworkInterfaces(interfaces);
    printNetworkInterfaces(interfaces);
    
    return 0;
}

std::string protocolName(int af) {
    switch (af) {
    case AF_INET:
        return "IPv4";
    case AF_INET6:
        return "IPv6";
    case AF_PACKET:
        return "Ethernet";
    default:
        return std::to_string(af);
    }
}


static void printNetworkInterfaces(const crane::Vec<NetworkInterface>& interfaces)
{
    for (const auto& interface : interfaces) {
        std::cout << interface.name <<std::endl;
        std::cout << "    " << interface.mac << std::endl;  
        for (const auto& address : interface.addresses) {
            std::cout << "    " << protocolName(address.family) ;
            std::cout << "    " << address.address << "/" << address.prefix ;
            std::cout<<std::endl;
        }
        std::cout<<"    ";
        if ( interface.up) std::cout << "up ";
        if ( interface.running) std::cout << "running " ;
        if ( interface.dynamic) std::cout << "dynamic " ;
        if ( interface.broadcast ) std::cout << "broadcast ";
        if ( interface.loopback) std::cout << "loopback ";
        if ( interface.multicast) std::cout << "multicast ";
        if ( interface.point_to_point) std::cout << "point-to-point ";
        std::cout<<std::endl;
    }
}

static NetworkInterface & addNetworkInterface(crane::Vec<NetworkInterface>& interfaces, const char * name)
{
    for ( auto & interface : interfaces ) {
        if ( strcmp(interface.name.c_str(), name) == 0 ) {
            return interface;
        }
    }

    NetworkInterface interface;
    interface.name = name;
    interfaces.pushBack(std::move(interface));
    return interfaces.back();
}   


static InterfaceAddress & addInterfaceAddress(NetworkInterface & interface, int af)
{
    /*
    for ( auto & address : interface.addresses ) {
        if ( address.family == af ) {
            return address;
        }
    }
    */

    interface.addresses.pushBack(InterfaceAddress{af});
    return interface.addresses.back();
}

static std::string socketAddressToString(const sockaddr * addr) 
{
    char addrbuf[128];
    const void * paddr = "";
    if ( addr->sa_family == AF_INET ) paddr = &((struct sockaddr_in *)addr)->sin_addr;
    else if ( addr->sa_family == AF_INET6 ) paddr = &((struct sockaddr_in6 *)addr)->sin6_addr;
    inet_ntop(addr->sa_family, paddr, addrbuf, sizeof(addrbuf));

    return std::string(addrbuf);
}

static int translateAddressToPrefix(struct sockaddr * mask) 
{
    int prefix = 0;
    if ( mask->sa_family == AF_INET ) {
        auto *pinaddr = (struct sockaddr_in *)mask;
        unsigned int mask = ntohl(pinaddr->sin_addr.s_addr);
        while (mask) {
            mask <<= 1;
            prefix++;
        }
    }
    else if ( mask->sa_family == AF_INET6 ) {
        auto paddr = &((struct sockaddr_in6 *)mask)->sin6_addr;
        u_char *bytes = (u_char *)paddr;
        unsigned int byte, bit;

        for (byte = 0; byte < sizeof(struct in6_addr); byte++, prefix += 8) {
            if (bytes[byte] != 0xff) break;
        }
        if (byte != sizeof(struct in6_addr)) {
            for (bit = 7; bit != 0; bit--, prefix++) {
                if (!(bytes[byte] & (1 << bit))) {
                    break;
                }
            }

            // 作为掩码，前面连续1，遇到一个0后，后面不能再有1
            for (; bit != 0; bit--) {
                if (bytes[byte] & (1 << bit)) {
                    prefix = 0;
                    break;
                }
            }
            if (prefix > 0) {
                byte++;
                for (; byte < sizeof(struct in6_addr); byte++) {
                    if (bytes[byte]) {
                        prefix = 0;
                    }
                }
            }
        }
    }
    return prefix;
}

// 生成mac地址字符串
static std::string hardwareAddressToString(const sockaddr_ll * addr)
{
    char buf[128];
    static const char hex[] = "0123456789abcdef";
    for (int i = 0; i < addr->sll_halen; i++) {
        buf[i * 3] = hex[(addr->sll_addr[i] >> 4) & 0x0f];
        buf[i * 3 + 1] = hex[addr->sll_addr[i] & 0x0f];
        buf[i * 3 + 2] = ':';
    }
    
    buf[addr->sll_halen * 3 - 1] = 0;
    return std::string(buf);
}

static void enumNetworkInterfaces(crane::Vec<NetworkInterface>& interfaces)
{
    struct ifaddrs *ifa, *origifa;

    if (getifaddrs(&origifa) != 0) { 
        assert(false);
    }

    for (ifa = origifa; ifa != NULL; ifa = ifa->ifa_next) {
        struct sockaddr *broadaddrP = NULL;

        // ignore non IPv4 addresses
        if (ifa->ifa_addr == NULL ) continue;

        int af = ifa->ifa_addr->sa_family;
        if (af != AF_INET && af != AF_INET6 && af != AF_PACKET ) continue;

        NetworkInterface & ni = addNetworkInterface(interfaces, ifa->ifa_name);

        if ( ifa->ifa_flags & IFF_POINTOPOINT ) ni.point_to_point = 1;         // net/if.h
        if ( ifa->ifa_flags & IFF_BROADCAST ) ni.broadcast = 1;                 // net/if.h
        if ( ifa->ifa_flags & IFF_LOOPBACK ) ni.loopback = 1;
        if ( ifa->ifa_flags & IFF_MULTICAST ) ni.multicast = 1;
        if ( ifa->ifa_flags & IFF_UP ) ni.up = 1;
        if ( ifa->ifa_flags & IFF_RUNNING ) ni.running = 1;
        if ( ifa->ifa_flags & IFF_DYNAMIC ) ni.dynamic = 1;

        if ( af == AF_PACKET ) {
            struct sockaddr_ll * paddr_ll = (struct sockaddr_ll*)ifa->ifa_addr;
            ni.mac = hardwareAddressToString(paddr_ll);
            continue;
        } 

        // 处理AF_INET和AF_INET6
        //
        InterfaceAddress & na = addInterfaceAddress(ni, ifa->ifa_addr->sa_family);

        char addrbuf[128];
        const void * paddr = "";
        if ( ifa->ifa_addr->sa_family == AF_INET ) paddr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
        else if ( ifa->ifa_addr->sa_family == AF_INET6 ) paddr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;

        na.address = socketAddressToString(ifa->ifa_addr);
        na.prefix  = translateAddressToPrefix(ifa->ifa_netmask);
        

        // add interface to the list
        //ifs = addif(env, sock, ifa->ifa_name, ifs, ifa->ifa_addr,
        //            broadaddrP, AF_INET,
        //            translateIPv4AddressToPrefix((struct sockaddr_in *)
        //                                         ifa->ifa_netmask));
        //
        // if an exception occurred then free the list
        //if ((*env)->ExceptionOccurred(env)) {
        //    freeifaddrs(origifa);
        //    freeif(ifs);
        //    return ;
        //}
    }

    // free ifaddrs buffer
    freeifaddrs(origifa);
}
